import math


def get_banded_flows(flows, lo_bound, hi_bound=-1):
    '''
    返回数据流集合flows中长度在[lo_bound, hi_bound]之间的数据流；
    如果hi_bound < 0，则返回所有长度大于等于lo_bound的数据流
    '''
    res = dict()
    for key, cnt in flows.items():
        if cnt >= lo_bound:
            if (hi_bound >= 0 and cnt <= hi_bound) or hi_bound < 0:
                res[key] = cnt
    return res


def hh_are_calc(真实流集, 测得流集, 阈值):
    # ARE, Average Relative Error
    '''
    本函数用于计数重流检测的平均相对误差, 并且同时考虑了
    在流集合1中被标记为重流但是在流集合2中未被标记为重流的情况,
    以及在流集合2中被标记为重流但是在流集合1中未被标记为重流的
    情况.
    '''
    真实重流集 = get_banded_flows(真实流集, 阈值, -1)
    测得重流集 = get_banded_flows(测得流集, 阈值, -1)
    误差 = 0.0
    for 标识符, 计数值 in 真实重流集.items():
        临时计数值 = 0
        if 标识符 in 测得重流集:
            临时计数值 = 测得重流集[标识符]
        # print("真实流长:%d, 估计流长:%d" % (计数值, 临时计数值))
        误差 += abs(计数值 - 临时计数值)/计数值
    误差 /= len(真实重流集)
    return 误差


def hh_f1score_calc(flows1, flows2, 阈值):
    '''
    首先在flows1中提取出长度在[lo_bound, hi_bound]中的数据流tempFlows1，之后
    再从flows2中提取出长度在[lo_bound, hi_bound]中的数据流tempFlows2，然后计算
    tempFlows1和tempFlows2的交集的容量
    '''
    tempFlows1 = get_banded_flows(flows1, 阈值, -1)
    tempFlows2 = get_banded_flows(flows2, 阈值, -1)
    c = 0
    for key in tempFlows1.keys():
        if key in tempFlows2:
            c = c + 1
    if 0 == len(tempFlows1):
        rr = 0
    else:
        rr = float(c)/len(tempFlows1)
    if 0 == len(tempFlows2):
        pr = 0
    else:
        pr = float(c)/len(tempFlows2)
    if 0 == pr + rr:
        f1score = 0
    else:
        f1score = 2*rr*pr/(pr + rr)
    return f1score


def get_topk_flows(流记录集, k=-1):
    if -1 == k:
        return 流记录集
    列表 = []
    for 标识符, 计数值 in 流记录集.items():
        列表.append((标识符, 计数值))
    列表.sort(key=(lambda x: x[1]), reverse=True)
    if k > len(流记录集):
        k = len(流记录集)
    临时流集 = dict()
    for i in range(k):
        临时流集[列表[i][0]] = 列表[i][1]
    return 临时流集


def topk_are_calc(真实流集, 测得流集, k):
    # ARE, Average Relative Error
    '''
    本函数用于计算最大的k个流的平均相对误差, 并且同时考虑了
    在流集合1中被标记为重流但是在流集合2中未被标记为重流的情况,
    以及在流集合2中被标记为重流但是在流集合1中未被标记为重流的
    情况.
    '''
    真实重流集 = get_topk_flows(真实流集, k)
    测得重流集 = get_topk_flows(测得流集, k)
    误差 = 0.0
    for 标识符, 计数值 in 真实重流集.items():
        临时计数值 = 0
        if 标识符 in 测得重流集:
            临时计数值 = 测得重流集[标识符]
        误差 += abs(计数值 - 临时计数值)/计数值
    误差 /= len(真实重流集)
    return 误差


def topk_f1score_calc(flows1, flows2, k):
    tempFlows1 = get_topk_flows(flows1, k)
    tempFlows2 = get_topk_flows(flows2, k)
    c = 0
    for key in tempFlows1.keys():
        if key in tempFlows2:
            c = c + 1
    if 0 == len(tempFlows1):
        rr = 0
    else:
        rr = float(c)/len(tempFlows1)
    if 0 == len(tempFlows2):
        pr = 0
    else:
        pr = float(c)/len(tempFlows2)
    if 0 == pr + rr:
        f1score = 0
    else:
        f1score = 2*rr*pr/(pr + rr)
    return f1score


def 计算流长度分布(流集, 阈值=0):
    '''长度在所给阈值以下的数据流将不会被考虑'''
    流长度分布 = dict()
    for 流长度 in 流集.values():
        if 流长度 >= 阈值:
            if 流长度 in 流长度分布:
                流长度分布[流长度] += 1
            else:
                流长度分布[流长度] = 1
    return 流长度分布


def 计算数据流长度方差(数据流集):
    临变1 = 0.0
    临变2 = 0.0
    for 流长度 in 数据流集.values():
        临变1 += 流长度
        临变2 += 流长度**2
    return 临变2/len(数据流集) - ((临变1/len(数据流集))**2)


def 求累积分布(流集, 上限):
    cdf = [0] * (上限 + 1)
    for 长度 in 流集.values():
        if 长度 <= 上限:
            cdf[长度] += 1
    for i in range(1, 上限 + 1):
        cdf[i] += cdf[i - 1]
    for i in range(上限 + 1):
        cdf[i] = float(cdf[i])/len(流集)
    return cdf


def 计算流长度分布误差(真实流集, 测得流集, 阈值=0):
    '''计算流长度分布的WMRE (Weighted Mean Relative Error)'''
    # print("真实流数:%d, 测得流数:%d" % (len(真实流集), len(测得流集)))
    真实流分布 = 计算流长度分布(真实流集, 阈值)
    测得流分布 = 计算流长度分布(测得流集, 阈值)
    # 数据流总数 = 0.0
    # for 数据流长度, 数据流数 in 真实流分布.items():
    #     数据流总数 += 数据流数
    误差 = 0.0
    for 流长度, 流数目 in 真实流分布.items():
        临时流数目 = 0
        if 流长度 in 测得流分布:
            临时流数目 = 测得流分布[流长度]
        误差 += abs(流数目 - 临时流数目)/float(流数目)
        # print("流数目:%d, 临时流数目:%d" % (流数目, 临时流数目))
    误差 = 误差/len(真实流分布)
    return 误差


def 计算流长度分布熵(数据流集, 阈值=0):
    # 只考虑长度大于等于"阈值"的数据流
    流长度分布 = 计算流长度分布(数据流集, 阈值)
    流总数 = 0.0
    for 数据流长度, 数据流数 in 流长度分布.items():
        流总数 += 数据流数
    熵值 = 0
    for 数据流长度, 流数量 in 流长度分布.items():
        熵值 += -1 * (流数量/流总数) * math.log(流数量/流总数)
    return 熵值


def 计算流长度分布熵误差(真实流集, 测得流集, 阈值=0):
    '''我们只考虑起真实长度大于所给阈值的数据流'''
    真实熵 = 计算流长度分布熵(真实流集, 阈值)
    测得熵 = 计算流长度分布熵(测得流集, 阈值)
    return abs(真实熵 - 测得熵)/真实熵


def 计算流长度熵(数据流集, 阈值=0):
    # 只考虑长度大于等于"阈值"的数据流
    流长度序列 = []
    总长度 = 0.0
    for 流长度 in 数据流集.values():
        if 流长度 >= 阈值 and 流长度 > 0:
            流长度序列.append(流长度)
            总长度 += 流长度
    熵值 = 0
    for 流长度 in 流长度序列:
        熵值 += -1*(流长度/总长度)*math.log(流长度/总长度)
    return 熵值


def 计算流长度熵误差(真实流集, 测得流集, 阈值=0):
    '''我们只考虑起真实长度大于所给阈值的数据流'''
    真实熵 = 计算流长度熵(真实流集, 阈值)
    测得熵 = 计算流长度熵(测得流集, 阈值)
    return abs(真实熵 - 测得熵)/真实熵


def wfsc_calc(真实流集, 测得流集, 阈值=0):  # Weighted Flow Set Coverage
    # 只会考虑真实长度大于等于"阈值"的数据流
    assert(isinstance(真实流集, dict) and isinstance(测得流集, dict))
    真实流集 = get_banded_flows(真实流集, 阈值)
    总包数 = sum(真实流集.values())
    res = 0.0
    for 标识符, 计数值 in 真实流集.items():
        if 标识符 in 测得流集:
            res += 计数值
    return res/总包数


def fsc_calc(真实流集, 测得流集, 阈值=0):
    assert(isinstance(真实流集, dict) and isinstance(测得流集, dict))
    assert(len(真实流集) > 0)
    真实流集 = get_banded_flows(真实流集, 阈值)
    数据流数 = 0
    for 标识符, 计数值 in 测得流集.items():
        if 标识符 in 真实流集:
            数据流数 += 1
    return 数据流数/len(真实流集)


def array_are_calc(基准序列, 对比序列):
    assert(len(基准序列) == len(对比序列))
    res = 0.0
    for 基准值, 对比值 in zip(基准序列, 对比序列):
        if 对比值 > 基准值:
            对比值 = 基准值
        res += abs(基准值 - 对比值)/float(基准值)
    res = res/len(基准序列)
    return res


def are_calc(真实流集, 测得流集, 阈值=0):  # 平均测量误差, Relative Measurement Error
    # 只会考虑真实长度大于等于"阈值"的数据流
    assert(isinstance(真实流集, dict) and isinstance(测得流集, dict))
    真实流集 = get_banded_flows(真实流集, 阈值)
    assert(len(真实流集) > 0)
    误差 = 0
    for 标识符, 流长度 in 真实流集.items():
        临时长度 = 0
        if 标识符 in 测得流集:
            临时长度 = 测得流集[标识符]
        误差 += abs(流长度 - 临时长度)/流长度
    误差 = float(误差)/len(真实流集)
    return 误差


def wre_calc(真实流集, 测得流集, 阈值=0):  # Weighted Relative Error
    # 只会考虑真实长度大于等于"阈值"的数据流
    assert(isinstance(真实流集, dict) and isinstance(测得流集, dict))
    真实流集 = get_banded_flows(真实流集, 阈值)
    assert(len(真实流集) > 0)
    总包数 = 0.0
    for 标识符, 计数值 in 真实流集.items():
        总包数 += 计数值
    误差 = 0
    for 标识符, 长度 in 真实流集.items():
        临时长度 = 0
        if 标识符 in 测得流集:
            临时长度 = 测得流集[标识符]
        误差 += abs(长度 - 临时长度)
    误差 = 误差/float(总包数)
    return 误差
